﻿Imports System.Runtime.CompilerServices
Imports System.Windows.Forms
Imports System.Windows.Forms.ListViewItem
Imports System.IO
Imports System.Threading.Tasks
Imports System.ComponentModel

Module ModuleExtension
    Public Delegate Function SetPropertyCallback(Of T)(ByRef Ctrl As Control, ByVal Value As T) As Boolean
    Public Delegate Function SetPropertyCallback(Of U, V)(ByRef Ctrl As Control, ByVal Value As U, ByVal p As V) As Boolean

    <Extension()> _
    Public Function SetText(ByRef Ctrl As Control, ByVal [Text] As String) As Boolean
        If Ctrl.InvokeRequired Then
            Dim d As New SetPropertyCallback(Of String)(AddressOf SetText)
            Return Ctrl.Invoke(d, New Object() {Ctrl, [Text]})
        Else
            Ctrl.Text = [Text]
            Return True
        End If
    End Function

    <Extension()> _
    Public Function SetEnable(ByRef Ctrl As Control, ByVal Value As Boolean) As Boolean
        If Ctrl.InvokeRequired Then
            Dim d As New SetPropertyCallback(Of Boolean)(AddressOf SetEnable)
            Return Ctrl.Invoke(d, New Object() {Ctrl, Value})
        Else
            Ctrl.Enabled = Value
            Return True
        End If
    End Function

    <Extension()> _
    Public Function SetChecked(ByRef Ctrl As CheckBox, ByVal Value As Boolean) As Boolean
        If Ctrl.InvokeRequired Then
            Dim d As New SetPropertyCallback(Of Boolean)(AddressOf SetChecked)
            Return Ctrl.Invoke(d, New Object() {Ctrl, Value})
        Else
            Ctrl.Checked = Value
            Return True
        End If
    End Function

    <Extension()> _
    Public Function SetTopMost(ByRef Ctrl As Form, ByVal Value As Boolean) As Boolean
        If Ctrl.InvokeRequired Then
            Dim d As New SetPropertyCallback(Of Boolean)(AddressOf SetTopMost)
            Return Ctrl.Invoke(d, New Object() {Ctrl, Value})
        Else
            Ctrl.TopMost = Value
            Return True
        End If
    End Function

    <Extension()> _
    Public Function AddItem(Of T)(ByRef Ctrl As ListBox, ByVal Item As T, Optional ByVal predictor As Predicate(Of T) = Nothing) As Boolean
        If predictor(Item) = False Then Return False
        If Ctrl.InvokeRequired Then
            Dim d As New SetPropertyCallback(Of Object, Predicate(Of T))(AddressOf AddItem)
            Return Ctrl.Invoke(d, New Object() {Ctrl, Item, predictor})
        Else
            Ctrl.Items.Add(Item)
            Return True
        End If
    End Function

    <Extension()> _
    Public Function RemoveItem(ByRef Ctrl As ListBox, ByVal Item As Object) As Boolean
        If Ctrl.InvokeRequired Then
            Dim d As New SetPropertyCallback(Of Object)(AddressOf RemoveItem)
            Return Ctrl.Invoke(d, New Object() {Ctrl, Item})
        Else
            Ctrl.Items.Remove(Item)
            Return True
        End If
    End Function

    <Extension()> _
    Public Function ToString(ByRef [Me] As IList, Optional ByVal spliter As String = ",") As String
        Dim str As String = ""
        For Each item As Object In [Me]
            str += item.ToString() + spliter
        Next item
        str = "{" + str.Trim().Substring(0, str.Trim().Length - 1) + "}"
        Return str
    End Function

    <Extension()> _
    Public Function ToString(ByRef item As ListViewItem, Optional ByVal delim As String = ", ") As String
        Dim lst As New List(Of String)
        lst.Add(item.Text)
        For Each i As ListViewSubItem In item.SubItems
            lst.Add(i.Text)
        Next i
        Return lst.ToArray().ToString(delim)
    End Function

    <Extension()> _
    Public Sub CopyTo(ByRef [Me] As DirectoryInfo, ByVal dst As String)
        Dim s = [Me]
        Dim d As New DirectoryInfo(dst)
        If s.FullName = d.FullName Then
            Return
        Else
            If Not d.Exists Then d.Create()
            Dim files = s.GetFiles()
            For Each file As FileInfo In files
                file.CopyTo(Path.Combine(d.FullName, file.Name), True)
            Next file
            Dim dirs = s.GetDirectories()
            For Each [dir] As DirectoryInfo In dirs
                Dim dst_dir = Path.Combine(d.FullName, dir.Name)
                dir.CopyTo(dst_dir)
            Next [dir]
        End If
    End Sub

    <Extension()> _
    Public Sub RemoveAll(ByRef colletion As System.Windows.Forms.ListBox.ObjectCollection)
        For Each item In colletion
            colletion.Remove(item)
        Next item
    End Sub

    <Extension()> _
    Public Function RemoveIf(Of T)(ByVal [Me] As System.Windows.Forms.ListBox.ObjectCollection, ByVal predict As Func(Of T, Boolean)) As Boolean
        For Each item In [Me]
            If predict(item) Then
                [Me].Remove(item)
                Return True
            End If
        Next
        Return False
    End Function

    <Extension()> _
    Public Function ToArray(Of T)(ByVal [Me] As System.Windows.Forms.ListBox.ObjectCollection) As T()
        Dim lst As New List(Of T)
        For Each item As Object In [Me]
            lst.Add(GenericCast(Of Object, T)(item))
        Next
        Return lst.ToArray()
    End Function

    <Extension()> _
    Public Sub Sort(Of T As IComparable(Of T))(ByVal [Me] As System.Windows.Forms.ListBox)
        If [Me].Items.Count = 0 Then Exit Sub
        Dim lst([Me].Items.Count - 1) As T
        [Me].Items.CopyTo(lst, 0)
        Array.Sort(lst)
        [Me].Items.Clear()
        For Each item In lst
            [Me].Items.Add(item)
        Next
    End Sub

    <Extension()> _
    Public Function RemoveIf(Of T)(ByVal [Me] As IList(Of T), ByVal predict As Func(Of T, Boolean)) As Boolean
        For Each item In [Me]
            If predict(item) Then
                [Me].Remove(item)
                Return True
            End If
        Next
        Return False
    End Function

    <Extension()> _
    Public Function Contains(Of T)(ByVal [Me] As IList(Of T), ByVal predict As Func(Of T, Boolean)) As Boolean
        For Each item In [Me]
            If predict(item) Then
                Return True
            End If
        Next
        Return False
    End Function

    <Extension()> _
    Public Function Contains(Of T)(ByVal [Me] As IList(Of T), ByVal predict As Func(Of T, Boolean), ByRef refItem As T) As Boolean
        For Each item In [Me]
            If predict(item) Then
                refItem = item
                Return True
            End If
        Next
        Return False
    End Function

    <Extension()> _
    Public Function GetMd5Hash(ByVal [Me] As FileInfo) As String
        Dim pathName As String = [Me].FullName
        Dim strResult As String = ""
        Dim strHashData As String = ""
        Dim arrbytHashValue As Byte()
        Dim fs As System.IO.FileStream = Nothing
        Dim MD5Hasher As New System.Security.Cryptography.MD5CryptoServiceProvider()
        Try
            fs = New System.IO.FileStream(pathName.Replace("""", ""), System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite)
            arrbytHashValue = MD5Hasher.ComputeHash(fs)
            fs.Close()
            strHashData = System.BitConverter.ToString(arrbytHashValue)
            strHashData = strHashData.Replace("-", "")
            strResult = strHashData
        Catch ex As System.Exception
            Throw ex
        End Try
        Return strResult
    End Function

    <Extension()> _
    Public Sub Cancel(ByVal [Me] As BackgroundWorker)
        If [Me].IsBusy = True Then
            [Me].CancelAsync()
            While [Me].IsBusy
                Application.DoEvents()
                Threading.Thread.Sleep(100)
            End While
        End If
    End Sub

    <Extension()> _
    Public Sub ParallelForEach(ByVal [Me] As DirectoryInfo, ByVal act As System.Action(Of FileInfo), ByVal ParamArray exts() As String)
        If [Me].Exists = True Then
            Dim targetFiles As New List(Of FileInfo)
            For Each item In exts
                targetFiles.AddRange([Me].EnumerateFiles("*" & item))
            Next
            Parallel.ForEach(targetFiles,
                Sub(item)
                    Call act(item)
                End Sub)
            Dim dirs = [Me].GetDirectories()
            For Each item In dirs
                ParallelForEach(item, act, exts)
            Next
        End If
    End Sub

    <Extension()> _
    Public Sub ForEach(ByVal [Me] As DirectoryInfo, ByVal act As System.Action(Of FileInfo), ByVal ParamArray exts() As String)
        If [Me].Exists = True Then
            Dim targetFiles As New List(Of FileInfo)
            If exts.Length > 0 Then
                For Each item In exts
                    targetFiles.AddRange([Me].EnumerateFiles("*" & item))
                Next
            Else
                targetFiles.AddRange([Me].EnumerateFiles())
            End If
            For Each item In targetFiles
                Call act(item)
            Next
            Dim dirs = [Me].GetDirectories()
            For Each item In dirs
                ForEach(item, act, exts)
            Next
        End If
    End Sub

End Module